package com.gyx.lottery.service.impl;

import com.gyx.lottery.common.errorcode.ServiceErrorCodeConstants;
import com.gyx.lottery.common.exception.ServiceException;
import com.gyx.lottery.common.utils.JacksonUtil;
import com.gyx.lottery.common.utils.RedisUtil;
import com.gyx.lottery.controller.param.CreateAcivityParam;
import com.gyx.lottery.controller.param.CreateActivityPrizeParam;
import com.gyx.lottery.controller.param.CreateActivityUserParam;
import com.gyx.lottery.controller.param.PageParam;
import com.gyx.lottery.dao.dataobject.ActivityDO;
import com.gyx.lottery.dao.dataobject.ActivityPrizeDO;
import com.gyx.lottery.dao.dataobject.ActivityUserDO;
import com.gyx.lottery.dao.dataobject.PrizeDO;
import com.gyx.lottery.dao.mapper.*;
import com.gyx.lottery.service.ActivityService;
import com.gyx.lottery.service.dto.ActivityCreateDTO;
import com.gyx.lottery.service.dto.ActivityDTO;
import com.gyx.lottery.service.dto.ActivityDetailDTO;
import com.gyx.lottery.service.dto.PageListDTO;
import com.gyx.lottery.service.enums.ActicityPrizeTiersEnum;
import com.gyx.lottery.service.enums.ActivityPrizeStatuseEnum;
import com.gyx.lottery.service.enums.ActivityStatusEnum;
import com.gyx.lottery.service.enums.ActivityUserStatusEnum;
import org.apache.ibatis.javassist.SerialVersionUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.rmi.server.ServerCloneException;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ActivityServiceImpl implements ActivityService {


    /**
     * 活动缓存redis
     */
    private static final Logger logger = LoggerFactory.getLogger(ActivityServiceImpl.class);
    /**
     * 设置活动缓存前缀，区分业务
     */
    private final String ACTIVITY_PREFIX="ACTIVITY_";
    /**
     * 设置缓存过期时间
     */
    private final Long ACTIVITY_TIMEOUT = 60*60*24*3L;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ActivityCreateDTO createActivity(CreateAcivityParam param) {
        //1.首先进行参数校验
            checkCreateActivityParam(param);
        //2.往数据库中保存数据
        //保存活动数据
        ActivityDO activityDO=new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
       activityMapper.insert(activityDO);
        //保存活动关联奖品数据
        List<ActivityPrizeDO>activityPrizeDOList=param.getActivityPrizeList().stream()
                .map(prizeParam->{
                    ActivityPrizeDO apDO=new ActivityPrizeDO();
                    apDO.setActivityId(activityDO.getId());
                    apDO.setPrizeId(prizeParam.getPrizeId());
                    apDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    apDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    apDO.setStatus(ActivityPrizeStatuseEnum.INIT.name());
                    return apDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);
        //保存活动关联人员数据
        List<ActivityUserDO>activityUserDOList=param.getActivityUserList().stream()
                .map(userParam->{
                    ActivityUserDO audo=new ActivityUserDO();
                    audo.setActivityId(activityDO.getId());
                    audo.setUserId(userParam.getUserId());
                    audo.setUserName(userParam.getUserName());
                    audo.setStatus(ActivityUserStatusEnum.INIT.name());
                  return audo;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);
        //3.往redis缓存中注入数据
        List<Long> prizeIds=param.getActivityPrizeList().stream()
                .map(CreateActivityPrizeParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<PrizeDO>prizeDOList=prizeMapper.batchSelectById(prizeIds);
                //构造抽奖活动需要的信息
        //活动奖品信息:奖品Id，奖品名称，奖品数量，奖品等级，奖品状态，人员id，人员姓名，人员状态，
        //activityDO,activityUserDOList,activityPrizeDOList,prizeDOList
        ActivityDetailDTO activityDetailDTO=convertToActivityDetailDTO(activityDO,activityPrizeDOList,activityUserDOList,prizeDOList);
            cacheActivityDetailDTO(activityDetailDTO);
        //4.构造结果并返回
        ActivityCreateDTO activityCreateDTO=new ActivityCreateDTO();
        activityCreateDTO.setActivityId(activityDO.getId());
        return activityCreateDTO;
    }

    @Override
    public PageListDTO<ActivityDTO> findList(PageParam param) {
        if(null == param){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_LIST_PARAM_EEOR);
        }
        Integer total=activityMapper.count();
        List<ActivityDO>activityDOList=activityMapper.
                selectByPage(param.getOffset(),param.getPageSize());
        List<ActivityDTO>activityDTOList=activityDOList.stream()
                .map(aDO->{
                    ActivityDTO activityDTO=new ActivityDTO();
                    activityDTO.setActivityId(aDO.getId());
                    activityDTO.setActivityName(aDO.getActivityName());
                    activityDTO.setDescription(aDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(aDO.getStatus()));
                        return activityDTO;
                }).collect(Collectors.toList());
        PageListDTO<ActivityDTO> pageListDTO=new PageListDTO<>(total,activityDTOList);
        return pageListDTO;
    }

    @Override
    public ActivityDetailDTO searchActivityDetail(Long activityId) {
        ActivityDetailDTO activityDetailDTO = getCacheActivityDetailDTO(activityId);
        if(activityDetailDTO!=null){
            return  activityDetailDTO;
        }
        //需要重新从数据库中进行读取
        //1.读取活动信息
        ActivityDO aDO=activityMapper.selectById(activityId);
        if(aDO==null){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_NOT_EXESIST_ERROR);
        }
        //2.读取活动关联奖品信息
       List<ActivityPrizeDO> aPDOList=activityPrizeMapper.selectById(activityId);
        if(aPDOList==null){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        //3.读取活动关联人员信息
        List<ActivityUserDO> aUDOList=activityUserMapper.selectById(activityId);
        if(aPDOList==null){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        //4.读取奖品信息
        List<Long>prizeIds=aPDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        if(prizeIds==null||prizeIds.size()==0){
            throw new ServiceException(ServiceErrorCodeConstants.PRIZE_IDS_NOT_EXESIST);
        }
        List<PrizeDO>pDOList=prizeMapper.batchSelectById(prizeIds);
        if(pDOList==null){
            throw new ServiceException(ServiceErrorCodeConstants.PRIZE_EXESIST_ERROR);
        }
        activityDetailDTO = convertToActivityDetailDTO(aDO, aPDOList, aUDOList, pDOList);

        if(activityDetailDTO==null){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_DETAIL_ERROR);
        }
        cacheActivityDetailDTO(activityDetailDTO);
        return activityDetailDTO;
    }

    /**
     * 缓存活动相关信息
     * @param activityId
     */
    @Override
    public void cacheAcitivity(Long activityId) {
        //1.读取活动信息
        ActivityDO aDO=activityMapper.selectById(activityId);
        if(aDO==null){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_NOT_EXESIST_ERROR);
        }
        //2.读取活动关联奖品信息
        List<ActivityPrizeDO> aPDOList=activityPrizeMapper.selectById(activityId);
        if(aPDOList==null){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        //3.读取活动关联人员信息
        List<ActivityUserDO> aUDOList=activityUserMapper.selectById(activityId);
        if(aPDOList==null){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        //4.读取奖品信息
        List<Long>prizeIds=aPDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        if(prizeIds==null||prizeIds.size()==0){
            throw new ServiceException(ServiceErrorCodeConstants.PRIZE_IDS_NOT_EXESIST);
        }
        List<PrizeDO>pDOList=prizeMapper.batchSelectById(prizeIds);
        if(pDOList==null){
            throw new ServiceException(ServiceErrorCodeConstants.PRIZE_EXESIST_ERROR);
        }
      ActivityDetailDTO  activityDetailDTO = convertToActivityDetailDTO(aDO, aPDOList, aUDOList, pDOList);
        cacheActivityDetailDTO(activityDetailDTO);
    }

    private void cacheActivityDetailDTO(ActivityDetailDTO activityDetailDTO) {
        if(null == activityDetailDTO||null == activityDetailDTO.getActivityId()){
            logger.warn("要缓存的活动信息不存在");
            return;
        }
        try {
            redisUtil.set(ACTIVITY_PREFIX + activityDetailDTO.getActivityId(),
                    JacksonUtil.writeVauleAsString(activityDetailDTO),
                    ACTIVITY_TIMEOUT);
        }
        catch (Exception e){
            logger.info("cacheActivityDetailDTO中缓存失败:activityDetailDTO:{}",
                    JacksonUtil.writeVauleAsString(activityDetailDTO),e);
        }
    }
    public ActivityDetailDTO getCacheActivityDetailDTO(Long activityId)
    {
        if(activityId==null){
            return  null;
        }
        String activityDetail = redisUtil.get(ACTIVITY_PREFIX + activityId);
        if(activityDetail==null){
            return  null;

        }
        ActivityDetailDTO activityDetailDTO = JacksonUtil.readVaule(activityDetail, ActivityDetailDTO.class);
        return activityDetailDTO;
    }

    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO, List<ActivityPrizeDO> activityPrizeDOList,
                                                         List<ActivityUserDO> activityUserDOList, List<PrizeDO> prizeDOList) {
        ActivityDetailDTO activityDetailDTO=new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDesc(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
        activityDetailDTO.setPrizeDTOList(
                activityPrizeDOList.stream().map(
                        auDO -> {
                            ActivityDetailDTO.PrizeDTO prizeDTO=new ActivityDetailDTO.PrizeDTO();
                            prizeDTO.setPrizeAmount(auDO.getPrizeAmount());
                            prizeDTO.setPrizeTiers(ActicityPrizeTiersEnum.forName(auDO.getPrizeTiers()));
                            prizeDTO.setStatus(ActivityPrizeStatuseEnum.forName(auDO.getStatus()));
                            prizeDTO.setPrizeId(auDO.getPrizeId());
                            Optional<PrizeDO> optionalPrizeDO =prizeDOList.stream()
                                    .filter(prizeDO -> prizeDO.getId().equals(prizeDTO.getPrizeId())).findFirst();
                            //prizeDO不为空执行当前方法
                            optionalPrizeDO.ifPresent(prizeDO -> {
                                prizeDTO.setImageUrl(prizeDO.getImageUrl());
                                prizeDTO.setName(prizeDO.getName());
                                prizeDTO.setPrice(prizeDO.getPrice());
                                prizeDTO.setDescription(prizeDO.getDescription());
                            });
                            return prizeDTO;
                        }
                ).collect(Collectors.toList())

        );
        activityDetailDTO.setUserDTOList(
                activityUserDOList.stream().map(
                        activityUserDO -> {
                            ActivityDetailDTO.UserDTO userDTO=new ActivityDetailDTO.UserDTO();
                            userDTO.setUserId(activityUserDO.getUserId());
                            userDTO.setUserName(activityUserDO.getUserName());
                            userDTO.setStatus(ActivityUserStatusEnum.forName(activityUserDO.getStatus()));
                            return userDTO;
                        }
                ).collect(Collectors.toList())
        );
        return activityDetailDTO;
    }

    /**
     * 校验活动信息是否合法
     * @param param 创建活动信息
     */

    private void checkCreateActivityParam(CreateAcivityParam param) {
        //校验参数是否为空
            if(null == param){
                throw new ServiceException(ServiceErrorCodeConstants.ACTICITY_INFO_EMPTY);
            }
        //校验奖品列表Id是否都能在数据库中查到
            List<Long> prizeIds=param.getActivityPrizeList().stream()
                    .map(CreateActivityPrizeParam::getPrizeId)
                    .distinct()
                    .collect(Collectors.toList());
            List<Long> exsistsPrizeIds=prizeMapper.findIdByIds(prizeIds);
            if(null == exsistsPrizeIds)
            {
                throw  new ServiceException(ServiceErrorCodeConstants.PRIZE_IDS_NOT_EXESIST);
            }
            if(CollectionUtils.isEmpty(exsistsPrizeIds)){
                throw  new ServiceException(ServiceErrorCodeConstants.PRIZE_IDS_NOT_EXESIST);
            }
            prizeIds.forEach(id->{
                if(!exsistsPrizeIds.contains(id))
                {
                    throw new ServiceException(ServiceErrorCodeConstants.PRIZE_IDS_NOT_EXESIST);
                }
            });
        //校验人员列表Id是否都能在数据库中查到
        List<Long> userIds=param.getActivityUserList().stream()
                .map(CreateActivityUserParam::getUserId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> exsistsUserIds=userMapper.findIdByIds(userIds);
        if(null == exsistsUserIds){
            throw  new ServiceException(ServiceErrorCodeConstants.USER_IDS_NOT_EXESIST);
        }
        if(CollectionUtils.isEmpty(exsistsUserIds)){
            throw new ServiceException(ServiceErrorCodeConstants.USER_IDS_NOT_EXESIST);
        }

        userIds.forEach(id->{
            if(!exsistsUserIds.contains(id))
            {
                throw new ServiceException(ServiceErrorCodeConstants.USER_IDS_NOT_EXESIST);
            }
        });


        //校验参加人员数量是否大于奖品数量
        long prizeSum = param.getActivityPrizeList().stream().
                mapToLong(CreateActivityPrizeParam::getPrizeAmount)
                .sum();
        long userSum=param.getActivityUserList().size();
        if(prizeSum>userSum){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PEIZE_USER_ERROR);
        }
        //校验奖品等级是否合法
        param.getActivityPrizeList().forEach(prize->{
            if(ActicityPrizeTiersEnum.forName(prize.getPrizeTiers())==null){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });
    }
}
